﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Windows.Input;
using Workbench.Models;
using Workbench.Operations;
using Workbench.Shared;

namespace Workbench.Presentation
{
    public class WordListViewModel : TabViewModelBase
    {
        #region Properties

        private ObservableCollection<WordListWordViewModel> words = new ObservableCollection<WordListWordViewModel>();
        public ObservableCollection<WordListWordViewModel> Words { get { return words; } }

        private SortingOption sortingOption;
        public SortingOption SortingOption
        {
            get { return sortingOption; }
            set { sortingOption = value; RaisePropertyChanged(); }
        }

        private bool isSorting;
        public bool IsSorting
        {
            get { return isSorting; }
            set { isSorting = value; RaisePropertyChanged(); }
        }

        private bool canExport;
        public bool CanExport
        {
            get { return canExport; }
            set { canExport = value; RaisePropertyChanged(); }
        }

        #endregion Properties

        public WordListViewModel()
            : base("Word List")
        {
            PropertyChanged += WordListViewModel_PropertyChanged;
            exportCSVCommand = new _ExportCSVCommand(this);
            ExportCSVCommand.CanExecuteChanged += ExportCSVCommand_CanExecuteChanged;
        }

        void ExportCSVCommand_CanExecuteChanged(object sender, EventArgs e)
        {
            CanExport = exportCSVCommand.CanExecute(null);
        }

        #region Event Handlers

        private async void WordListViewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "SortingOption":
                    var wordsList = Words.ToList();
                    words.Clear();
                    IsSorting = true;
                    await SortWordsAsync(wordsList, SortingOption);
                    words.AddRange(wordsList);
                    IsSorting = false;
                    break;
            }
        }

        public override void Unload()
        {
            Words.Clear();
        }

        public override void Load()
        {
            Words.Clear();
            var rootDirectory = ProjectOperations.GetRootDirectory();
            var files = rootDirectory.GetContextIncludedFiles();
            LoadWords(files);
        }

        #endregion Event Handlers

        #region Action

        public async void LoadWords(IEnumerable<ProjectFileModel> files)
        {
            IsSorting = true;
            var allWords = await WordListOperations.GetAllUniqueWordsAsync(files);
            var wordViewModels = allWords.Select(x => new WordListWordViewModel(x)).ToList();

            await SortWordsAsync(wordViewModels, SortingOption);
            words.AddRange(wordViewModels);
            IsSorting = false;
        }

        public async void ExportWords(string filePath)
        {
            try
            {
                await WordListOperations.ExportCSV(filePath, Words.Select(x => x.dataModel));
            }
            catch (IOException ex)
            {
                string message = string.Format("You can not overwrite file: {0}. It's in use by another program", filePath);
                LoggingService.LogError(ex);
                RaiseError(message);
            }
        }

        public Task SortWordsAsync(List<WordListWordViewModel> words, SortingOption sortingOption)
        {
            return Task.Run(() =>
            {
                if (words.Count == 0) return;

                switch (sortingOption)
                {
                    case SortingOption.Frequency:
                        words.Sort((x, y) => { return -x.Freq.CompareTo(y.Freq); });
                        break;
                    case SortingOption.WordEnd:
                        words.Sort((x, y) => { return string.Compare(x.ReverseContent, y.ReverseContent, StringComparison.CurrentCulture); });
                        break;
                    case SortingOption.WordStart:
                        words.Sort((x, y) => { return string.Compare(x.Content, y.Content, StringComparison.CurrentCulture); });
                        break;
                }

                int rank = 1;
                words[0].Rank = rank;
                for (int i = 1; i < words.Count; i++)
                {
                    words[i].Rank = (words[i].Freq == words[i - 1].Freq) ? rank : ++rank;
                }
            });
        }

        #endregion Action

        #region Commands

        #region ExportCSVCommand

        readonly _ExportCSVCommand exportCSVCommand;
        public ICommand ExportCSVCommand
        {
            get { return exportCSVCommand; }
        }

        private class _ExportCSVCommand : ICommand
        {
            readonly WordListViewModel viewModel;

            public _ExportCSVCommand(WordListViewModel viewModel)
            {
                this.viewModel = viewModel;

                viewModel.Words.CollectionChanged += Words_CollectionChanged;
                viewModel.PropertyChanged += viewModel_PropertyChanged;
            }

            private void Words_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
            {
                RaiseCanExecuteChanged();
            }

            private void viewModel_PropertyChanged(object sender, PropertyChangedEventArgs e)
            {
                if(string.Compare(e.PropertyName, "IsLoading", true) == 0
                || string.Compare(e.PropertyName, "IsSorting", true) == 0)
                    RaiseCanExecuteChanged();
            }

            void RaiseCanExecuteChanged()
            {
                this.CanExecuteChanged(null, EventArgs.Empty);
            }

            public bool CanExecute(object parameter)
            {
                return viewModel.Words.Count > 0 && !viewModel.IsSorting && !viewModel.IsLoading;
            }

            public void Execute(object parameter)
            {
                var filePath = parameter as string;
                if(filePath != null) 
                    viewModel.ExportWords(filePath);
            }

            public event EventHandler CanExecuteChanged = (sender, args) => { };
        }

        #endregion ExportCSVCommand

        #endregion Commands
    }

    public enum SortingOption
    {
        Frequency,
        WordStart,
        WordEnd
    }

    public class WordListWordViewModel : ViewModelBase
    {
        public WordListWordViewModel(AggregatedWordModel dataModel)
        {
            this.dataModel = dataModel;

            Freq = dataModel.Freq;
            Content = dataModel.WordContent;
            ReverseContent = StringUtility.Reverse(dataModel.WordContent);

            if (dataModel.BaseForms.Count > 0)
            {
                LemmaForms = dataModel.GetConcatenatedBaseForms();
            }
        }

        public readonly AggregatedWordModel dataModel;

        private int rank;
        public int Rank
        {
            get { return rank; }
            set { rank = value; RaisePropertyChanged(); }
        }

        private int freq;
        public int Freq
        {
            get { return freq; }
            set { freq = value; RaisePropertyChanged(); }
        }

        private string content;
        public string Content
        {
            get { return content; }
            set { content = value; RaisePropertyChanged(); }
        }

        private string rcontent;
        public string ReverseContent
        {
            get
            {
                return rcontent;
            }
            set
            {
                rcontent = value;
            }
        }

        private string lemmaForms;
        public string LemmaForms
        {
            get { return lemmaForms; }
            set { lemmaForms = value; RaisePropertyChanged(); }
        }

        public override string ToString()
        {
            return string.Format("Content={0}, Freq={1}", Content, Freq);

        }
    }
}
